# DialectBuilder.java

    package org.codefilarete.stalactite.engine;

    import java.util.Set;
    import java.util.function.Function;

    import org.codefilarete.stalactite.query.builder.DMLNameProvider;
    import org.codefilarete.stalactite.query.builder.QuerySQLBuilderFactory;
    import org.codefilarete.stalactite.query.api.Fromable;
    import org.codefilarete.stalactite.query.api.Selectable;
    import org.codefilarete.stalactite.sql.DMLNameProviderFactory;
    import org.codefilarete.stalactite.sql.DatabaseSequenceSelectorFactory;
    import org.codefilarete.stalactite.sql.Dialect;
    import org.codefilarete.stalactite.sql.Dialect.DialectSupport;
    import org.codefilarete.stalactite.sql.DialectOptions;
    import org.codefilarete.stalactite.sql.GeneratedKeysReaderFactory;
    import org.codefilarete.stalactite.sql.QuerySQLBuilderFactoryBuilder;
    import org.codefilarete.stalactite.sql.ddl.DDLSequenceGenerator;
    import org.codefilarete.stalactite.sql.ddl.DDLTableGenerator;
    import org.codefilarete.stalactite.sql.ddl.SqlTypeRegistry;
    import org.codefilarete.stalactite.sql.statement.DMLGenerator;
    import org.codefilarete.stalactite.sql.statement.ReadOperationFactory;
    import org.codefilarete.stalactite.sql.statement.WriteOperationFactory;
    import org.codefilarete.stalactite.sql.statement.binder.ColumnBinderRegistry;
    import org.codefilarete.tool.collection.CaseInsensitiveSet;

    public class DialectBuilder {

    	protected final DatabaseVendorSettings vendorSettings;
    	protected final DialectOptions dialectOptions;

    	public DialectBuilder(DatabaseVendorSettings vendorSettings) {
    		this(vendorSettings, DialectOptions.noOptions());
    	}

    	public DialectBuilder(DatabaseVendorSettings vendorSettings, DialectOptions dialectOptions) {
    		this.vendorSettings = vendorSettings;
    		this.dialectOptions = dialectOptions;
    	}

    	public Dialect build() {
    		SqlTypeRegistry sqlTypeRegistry = buildSqlTypeRegistry();

    		ColumnBinderRegistry columnBinderRegistry = buildColumnBinderRegistry();

    		DMLNameProviderFactory dmlNameProviderFactory = buildDmlNameProviderFactory();

    		SQLOperationsFactories sqlOperationsFactories = vendorSettings.getSqlOperationsFactoriesBuilder()
    				.build(columnBinderRegistry, dmlNameProviderFactory, sqlTypeRegistry);

    		DDLTableGenerator ddlTableGenerator = sqlOperationsFactories.getDdlTableGenerator();
    		DDLSequenceGenerator ddlSequenceGenerator = sqlOperationsFactories.getDdlSequenceGenerator();
    		DMLGenerator dmlGenerator = sqlOperationsFactories.getDmlGenerator();
    		WriteOperationFactory writeOperationFactory = sqlOperationsFactories.getWriteOperationFactory();
    		ReadOperationFactory readOperationFactory = sqlOperationsFactories.getReadOperationFactory();
    		DatabaseSequenceSelectorFactory databaseSequenceSelectorFactory = sqlOperationsFactories.getSequenceSelectorFactory();

    		QuerySQLBuilderFactory querySQLBuilderFactory = createQuerySQLBuilderFactoryBuilder(dmlNameProviderFactory, columnBinderRegistry)
    				.build();

    		GeneratedKeysReaderFactory generatedKeysReaderFactory = vendorSettings.getGeneratedKeysReaderFactory();

    		return new DialectSupport(
    				vendorSettings.getCompatibility(),
    				ddlTableGenerator,
    				ddlSequenceGenerator,
    				dmlGenerator,
    				writeOperationFactory,
    				readOperationFactory,
    				querySQLBuilderFactory,
    				sqlTypeRegistry,
    				columnBinderRegistry,
    				dmlNameProviderFactory,
    				dialectOptions.getInOperatorMaxSize().getOrDefault(vendorSettings.getInOperatorMaxSize()),
    				generatedKeysReaderFactory,
    				databaseSequenceSelectorFactory,
    				vendorSettings.supportsTupleCondition()
    		);
    	}

    	protected QuerySQLBuilderFactoryBuilder createQuerySQLBuilderFactoryBuilder(DMLNameProviderFactory dmlNameProviderFactory, ColumnBinderRegistry columnBinderRegistry) {
    		return new QuerySQLBuilderFactoryBuilder(
    				dmlNameProviderFactory,
    				columnBinderRegistry,
    				vendorSettings.getJavaTypeToSqlTypes());
    	}

    	protected ColumnBinderRegistry buildColumnBinderRegistry() {
    		ColumnBinderRegistry columnBinderRegistry = new ColumnBinderRegistry(vendorSettings.getParameterBinderRegistry());
    		if (dialectOptions.getJavaTypeBinders().isTouched()) {
    			dialectOptions.getJavaTypeBinders().consumeIfTouched(typeBindings -> {
    				typeBindings.forEach(typeBinding -> {
    					columnBinderRegistry.register((Class) typeBinding.getJavaType(), typeBinding.getParameterBinder());
    				});
    			});
    		}
    		return columnBinderRegistry;
    	}

    	protected SqlTypeRegistry buildSqlTypeRegistry() {
    		SqlTypeRegistry sqlTypeRegistry = new SqlTypeRegistry(vendorSettings.getJavaTypeToSqlTypes());
    		if (dialectOptions.getJavaTypeToSqlTypeMappings().isTouched()) {
    			dialectOptions.getJavaTypeToSqlTypeMappings().consumeIfTouched(typeBindings -> {
    				typeBindings.forEach(typeBinding -> {
    					sqlTypeRegistry.put(typeBinding.getJavaType(), typeBinding.getSqlType());
    				});
    			});
    		}
    		return sqlTypeRegistry;
    	}

    	protected DMLNameProviderFactory buildDmlNameProviderFactory() {
    		Set<String> keywords = new CaseInsensitiveSet(vendorSettings.getKeywords());
    		dialectOptions.getSqlKeywordsToAdd().consumeIfTouched(keywords::addAll);
    		dialectOptions.getSqlKeywordsToRemove().consumeIfTouched(keywords::removeAll);

    		return dialectOptions.getQuoteSQLIdentifiers().getOrDefault(false)
    				? QuotingDMLNameProvider::new
    				: tableAliaser -> new QuotingKeywordsDMLNameProvider(tableAliaser, keywords);
    	}

    	protected class QuotingKeywordsDMLNameProvider extends QuotingDMLNameProvider {

    		private final CaseInsensitiveSet keywords;

    		public QuotingKeywordsDMLNameProvider(Function<Fromable, String> tableAliaser, Set<String> keywords) {
    			super(tableAliaser);
    			this.keywords = new CaseInsensitiveSet(keywords);
    		}

    		@Override
    		protected String quote(String name) {
    			if (keywords.contains(name)) {
    				return super.quote(name);
    			} else {
    				return name;
    			}
    		}
    	}

    	protected class QuotingDMLNameProvider extends DMLNameProvider {

    		private final char quoteCharacter;

    		public QuotingDMLNameProvider(Function<Fromable, String> tableAliaser) {
    			super(tableAliaser);
    			this.quoteCharacter = dialectOptions.getQuoteCharacter().getOrDefault(vendorSettings.getQuoteCharacter());
    		}

    		@Override
    		public String getSimpleName(Selectable<?> column) {
    			return quote(super.getSimpleName(column));
    		}

    		@Override
    		public String getName(Fromable table) {
    			return quote(super.getName(table));
    		}

    		@Override
    		public String getAlias(Fromable table) {
    			String alias = super.getAlias(table);
    			return alias == null ? null : quote(alias);
    		}

    		protected String quote(String name) {
    			return quoteCharacter + name + quoteCharacter;
    		}
    	}
    }